package org.tulkas.base;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.tulkas.util.IFlushable;

/**
 * This class is a Manager class that handles a set of IFlusher. It will check every flusher in a fixed interval.
 * If the flusher need flushing, flush it. <BR/>
 * The interval of the FlusherManager is configured in the key:
 * flusher.manager.interval (unit: second)
 * The interval of every IFlusher can be setup when the IFlusher is added to the FlusherManager.
 * 
 * @see org.tulkas.robot.base.IFlusher
 * @author <a href="mailto:tulkas.web@gmail.com">Tulkas</a>
 *
 */
public final class FlusherManager {
	private volatile static FlusherManager instance = null;

	private final List<FlusherItem> flushers = new ArrayList<FlusherItem>();
	private final FlusherChecker checker = new FlusherChecker();

	private FlusherManager() {
		start();
	}

	public static FlusherManager getInstance() {
		if (instance == null)
			synchronized (FlusherManager.class) {
				if (instance == null) {
					instance = new FlusherManager();
				}
			}
		return instance;
	}

	public static void destroyInstance() {
		synchronized (FlusherManager.class) {
			if (instance != null) {
				instance.destroy();
			}
			instance = null;
		}
	}

	/* (non-Javadoc)
	 * @see org.tulkas.robot.base.IManager#destroy()
	 */
	private synchronized void destroy() {
		stop();
		flushers.clear();
	}

	/**
	 * Stop the FlusherManager, close the checking thread.
	 */
	public synchronized void stop() {
		if (checker != null) {
			checker.stop();
		}
		flushers.clear();
	}

	/**
	 * Start the FlusherManager, open the checking thread.
	 */
	public synchronized void start() {
		checker.start();
	}

	/**
	 * Force to flush all the IFlushers.
	 */
	public synchronized void flushAll() {
		Iterator<FlusherItem> it = flushers.iterator();
		while (it.hasNext()) {
			FlusherItem item = it.next();
			IFlushable flusher = item.flusher;
			if (flusher.needFlush()) {
				flusher.flush();
				item.lastActivated = new Date();
			}
		}
	}

	public synchronized void addFlusher(IFlushable flusher, int interval) {
		FlusherItem item = new FlusherItem();
		item.flusher = flusher;
		item.interval = interval;
		item.lastActivated = new Date();
		this.flushers.add(item);
	}

	public synchronized void removeFlusher(IFlushable flusher) {
		FlusherItem removeItem = null;
		for (FlusherItem item : flushers) {
			if (item.flusher == flusher) {
				removeItem = item;
				break;
			}
		}
		if (removeItem != null)
			flushers.remove(removeItem);
	}

	static class FlusherItem {
		IFlushable flusher;

		Date lastActivated;

		int interval;
	}

	class FlusherChecker implements Runnable {
		private boolean running = false;
		private int interval;

		private Thread thread = null;

		public FlusherChecker() {
			interval = GlobalConfig.getInstance().getInt(
					"flusher.manager.interval", 1) * 1000;
		}

		public synchronized void start() {
			running = true;
			thread = new Thread(this, "FlusherChecker");
			thread.start();
		}

		public synchronized void stop() {
			this.running = false;
		}

		public synchronized boolean isRunning() {
			return running;
		}

		public void run() {
			while (true) {
				synchronized (this) {
					if (!running)
						break;
				}

				synchronized (FlusherManager.this) {
					Iterator<FlusherItem> it = flushers.iterator();
					while (it.hasNext()) {
						FlusherItem item = it.next();
						IFlushable flusher = item.flusher;
						if (flusher.needFlush()) {
							Date time = new Date();
							if (time.getTime() - item.lastActivated.getTime() > item.interval * 1000) {
								flusher.flush();
								item.lastActivated = time;
							}
						}
					}
				}

				try {
					Thread.sleep(interval);
				} catch (InterruptedException e) {
				}
			}
		}
	}

}
